<?php
/**
 * *********************************************************************************************************************
 * 申通新平台巴枪上传
 * *********************************************************************************************************************
 */
namespace soa\partner\sto;

class StoPda
{
    const TID = 'KB1111111111111';

    const ORG = '900000';

    const EMP = '9000000615';

    const PWD = '1234';

    public static $hostName = "http://pda.sto.cn:2228/";

    public static $methods = [
        'login'  => 'receive/plogin.action',    //登录
        'upload' => 'receive/message.action',   //上传
    ];

    public static $sessionId = null;

    public static $devId = null;

    public static $devImei = null;

    public static $branchCode = null;

    public static $checkCode = null;

    /**
     * 请求公共方法
     * @param $api
     * @param $data
     * @param string $type
     * @return mixed
     */
    private static function request($api, $data, $type = '')
    {
        if (empty($data)) {
            self::$errMsg = '请求数据不能为空';
            return null;
        }

        $datalen = mb_strlen($data, 'utf-8');
        $len = strlen($datalen);
        if ($len > 6) {   //字符串长度最多为6位数
            self::$errMsg = '请求字符超过长度限制';
            return null;
        }

        $strLength = str_repeat(0, 6 - $len) . $datalen;   //json字符串长度 总共6位 不够左边补0
        $checkInfo = self::$checkCode ? 'checkCode:' . self::$checkCode : '';
        $headers = ['Content-Type: text/xml;charset=UTF-8', 'Content-Encoding: gzip', $checkInfo];
        $options = ['HTTPHEADER' => $headers, 'TIMEOUT' => 10];

        $t1 = time();
        //申通巴枪数据上传
        $response = \artisan\http::post($api, gzencode($strLength . $data), $options);  //请求数据要先gzip压缩
        $deResult = gzdecode($response);    //gzip解压缩

        if($type == 'upload'){
            //打印所有申通上传的返回结果
//            logDebug($deResult,'sto_up_result',time() - $t1,true);
        }

        if(empty($response) || !$deResult){
            if($type == 'upload'){
                logDebug($deResult,'sto_up_return',time() - $t1);
            }
        }

        if($type == 'upload'){
//            logDebug($response, 'sto_up_return1', time() - $t1);
//            logDebug($deResult, 'sto_up_return2', time() - $t1);
        }

        if (!$deResult) {
            self::$errMsg = '上传接口无响应，请稍后重试';
            return null;
        }

        $resultData = json_decode(mb_substr($deResult, 6, null, 'utf-8'), true);   //返回数组
        if (json_last_error() != JSON_ERROR_NONE) {
            logDebug(json_encode($resultData), 'sto_pda_api_error_response_of_json_decode');
            self::$errMsg = '响应数据解析有误';
            return null;
        }

        if ($resultData['respCode'] == '001') {
            \artisan\cache::connect('pda')->expire('sto:PDA:sessionId', 0);
            logDebug(json_encode($resultData), 'sto_pda_api_error_response_of_not_001');
        } elseif ($resultData['respCode'] != '000') {
            logDebug(json_encode($resultData), 'sto_pda_api_error_response');
            if (!empty($resultData['errorRecord']) && is_array($resultData['errorRecord'])) {
                foreach ($resultData['errorRecord'] as $item) {
                    if (empty($item['respWaybillNo']) || empty($item['respErrorDescription'])) {
                        continue;
                    }
                    self::$errMsg[$item['respWaybillNo']] = $item['respErrorDescription'];
                }
            }
        }

        return $resultData;
    }

    /**
     * 登录
     * @return bool|mixed|null|string
     */
    public static function login()
    {
        $method = self::$methods['login'];      //接口
        $sessionId = \artisan\cache::connect('pda')->get('sto:PDA:sessionId'); //尝试从redis中获取sessionId
        if ($sessionId) {
            return $sessionId;
        }

        $getOperateCode = self::getOperateCode(10);    //获取业务所需操作码
        if (empty($getOperateCode)) {
            self::$errMsg = '操作码有误';
            return null;
        }

        $data = [
            'tid'            => self::TID,     //head中终端设备ID (巴枪Id)
            'trace'          => '',             //包的唯一识别码 可为空
            'mty'            => $getOperateCode['mty'],//操作码
            'opOrgCode'      => self::ORG,       //操作网点编码
            'deviceType'     => 'PDA',          //设备类型
            'opTerminal'     => self::TID,     //操作终端(tid)(巴枪Id)
            'empCode'        => self::EMP,   //员工工号
            'pdaPassword'    => md5(self::PWD),         //密码
            'verifyDate'     => date('Y-m-d H:i:s'),   //登录日期
            'connectionType' => 2,              //登录连接方式
            'os'             => '01',           //操作系统
            'osVersion'      => '0.0.0.0',      //系统版本
            'softVersion'    => '0.0.0.0',      //软件版本
            'pdaType'        => '70W',          //设备型号
        ];

        $must = [
            'tid'            => "head中终端设备id",
            'mty'            => "操作码",
            'opOrgCode'      => "操作网点编码",
            'deviceType'     => "设备类型",
            'opTerminal'     => "操作终端(tid)",
            'empCode'        => "员工工号",
            'pdaPassword'    => "密码",
            'verifyDate'     => "登录日期",
            'connectionType' => "登录连接方式",
            'softVersion'    => "软件版本",
            'os'             => "操作系统",
            'osVersion'      => "系统版本",
            'pdaType'        => "设备型号",
        ];

        $mustVerify = self::mustFieldVerify($must, $data);    //必填项校验
        if ($mustVerify) {
            self::$errMsg = $mustVerify;
            return null;
        }

        $api = self::$hostName . $method; //完整接口地址
        $response = self::request($api, json_encode($data));    //发送请求
        $sessionId = $response['sessionId'];
        if (empty($sessionId)) {
            self::$errMsg = "sto_sessionId返回异常";
            return null;
        }

        \artisan\cache::connect('pda')->set('sto:PDA:sessionId', $sessionId, 3600 * 20); //sessionId存入redis 有效期20小时
        return $sessionId;
    }

    /**
     * $waybillType 1：揽件  2：派件   3、签收 4：问题件 5: 到件, 6:发件, 7:退件, 8:第三方签收
     * @param $waybillType
     * @param $waybillData
     * @param $devId
     * @param $devImei
     * @param $branchCode
     * @param $checkCode
     * @param int $isCorn
     * @return array
     */
    public static function upload($waybillType, $waybillData, $devId, $devImei, $branchCode, $checkCode, $isCorn = 0)
    {
        $method = self::$methods['upload'];
        $sessionId = \artisan\cache::connect('pda')->get('sto:PDA:sessionId'); //尝试从redis中获取sessionId
        if (empty($sessionId)) {
            $sessionId = self::login();
            if (!is_string($sessionId) || empty($sessionId)) {
                self::$errMsg = "sessionId获取失败";
                return null;
            }
        }

        self::$sessionId = $sessionId;
        self::$devId = $devId;
        self::$devImei = $devImei;
        self::$branchCode = $branchCode;
        self::$checkCode = $checkCode;

        $param = self::getRequestData($waybillType, $waybillData);  //获取请求数据
        if(empty($param)){
            return null;
        }
//logDebug($param, 'sto_upload');
        $api = self::$hostName . $method; //完整接口地址
        $response = self::request($api, json_encode($param), 'upload');    //发送请求

        if ($response['respCode'] == '001') { //sessionId失效，清除缓存的sessionId再试一遍
            $sessionId = self::login();
            if ($sessionId && is_string($sessionId)) {
                $param['sessionId'] = self::$sessionId = $sessionId;
                $response = self::request($api, json_encode($param),'upload');
            }
        }
        if ($isCorn) {
            logDebug($param, 'sto_upload');
        }
//        logDebug($response, 'sto_upload');
        return $response;
    }

    /**
     * 获取请求数据
     * @param $waybillType
     * @param $waybillData
     * @return array
     */
    public static function getRequestData($waybillType, $waybillData)
    {
        $getOperateCode = self::getOperateCode($waybillType);  //获取业务所需操作码
        if (empty($getOperateCode)) {
            self::$errMsg = '操作码有误';
            return null;
        }

        $param = $tmp = [
            "cnt"        => count($waybillData),            //数据包内记录的计数
            "deviceType" => "PDA",          //设备类型  PDA/PC
            "mty"        => $getOperateCode['mty'],   //操作码，“0”代表请求，“1”代表应答
            "opOrgCode"  => self::$branchCode ?: self::ORG,       //操作网点编码
            "opTerminal" => self::$devId,     //操作终端(tid) 终端唯一码
            //            "org"        => self::ORG,       //操作组织(测试环境用)
            "org"        => self::$branchCode ?: self::ORG, //操作组织(生产环境用)
            "sessionId"  => self::$sessionId,
            "tid"        => self::TID,     //head中终端设备ID
            "trace"      => "",             //包的唯一识别码   可为空
            "opRecord"   => [],
        ];

        self::$sendData = [];

        foreach ($waybillData as $v) {
            $opRecord = self::mergeParams($waybillType, $v);    //拼装业务所需参数
            if(empty($opRecord)){
                return null;
            }
            $param['opRecord'][] = $opRecord;
            $tmp['opRecord'][0] = $opRecord;
            self::$sendData[$v['waybill_no']] = json_encode($tmp,JSON_UNESCAPED_UNICODE);
        }

        return $param;
    }

    public static $sendData = [];

    /**
     * 拼装业务所需参数
     * @param $waybillType
     * @param $data
     * @return array
     */
    public static function mergeParams($waybillType, $data)
    {
        $pdaUuid = self::guid();   //生成随机码
        if (empty($pdaUuid)) {
            self::$errMsg = 'padUuid生成失败';
            return null;
        }

        $opCode = self::getOperateCode($waybillType);    //操作码
        if (empty($opCode)) {
            self::$errMsg = '操作码有误';
            return null;
        }

        $param = [
            "pdaUuid"           => $pdaUuid,           //32位随机码
            "goodsType"         => $data['resType'] ?: '',   //物品类别
            "effectiveType"     => "",                 //快件类型
            //            "desAreaCode"        => self::ORG, //目的地编码(测试环境用)
            //               "desAreaCode"        => $this->getBranchCode(), //要改成目的地编码？
            "waybillNo"         => $data['waybill_no'], //单号
            "expType"           => "10",               //单号类型
            //                    "sendMobilePhone"    => "",
            //                    "receiveMobilePhone" => "",
            //            "opOrgCode"         => self::ORG,           //操作网点编码
            "opOrgCode"         => self::$branchCode ?: self::ORG,
            "opCode"            => $opCode['opCode'],  //操作码
            "auxOpCode"         => "NEW",              //辅助操作码 包含NEW/DELETED
            "deviceType"        => "PDA",              //设备类型  PDA/PC
            "clientProgramRole" => "0",                //PDA程序角色
            "opTerminal"        => self::$devId,         //操作终端(tid)
            "opUserCode"        => $data['cm_code'] ?: $data['operator_code'],//操作人编码
            "opTime"            => $data['scan_time'],//操作时间
        ];

        //必填项
        $must = [
            'pdaUuid'    => "PDA_UUID",
            'waybillNo'  => "单号",
            'expType'    => "单号类型",
            'opOrgCode'  => "操作网点编码",
            'opCode'     => "操作码",
            'auxOpCode'  => "辅助操作码",
            'deviceType' => "设备类型",
            //            'clientProgramRole' => "PDA程序角色",
            'opTerminal' => "操作终端(tid)",
            'opUserCode' => "操作人编码",
            'opTime'     => "操作时间",
            //            'goodsType'  => "物品类别",
        ];

        switch ($waybillType) {
            case 1: //揽件
                $param['orderNo'] = '';
                $param['sendMobilePhone'] = '';
                $param['receiveMobilePhone'] = '';
                $param['desAreaCode'] = '';
                $param['weight'] = $data['weight'];
                $param['empCode'] = $data['operator_code'] ?: $data['cm_code']; //派件员

                $must['goodsType'] = "物品类别";
                break;
            case 2: //派件
                $param['interfaceversion'] = '1.0';
                $param['frequencyNo'] = ''; //班次(扫描客户端用)
                $param['weight'] = '';  //重量
                $param['inputWeight'] = ''; //输入重(扫描客户端用)
                $param['empCode'] = $data['operator_code'] ?: $data['cm_code']; //派件员

                //必填项
                $must['interfaceversion'] = "接口版本";
                $must['empCode'] = "派件员编号";
                break;
            case 3: //签收
                $param['recieverSignoff'] = $data['sign_type'];   //签收类型  图片签收/签收人名
                if ($data['signPic']) {
                    $param['signoffImg'] = $data['signPic'];   //签收图片

                    //必填项
                    $must['signoffImg'] = "签收图片";
                }
                $param['empCode'] = $data['cm_code']; //派件员

                //必填项
                $must['recieverSignoff'] = "签收人";
                $must['empCode'] = "派件员编号";
                break;
            case 4: //问题件
                $param['issueType'] = $data['bad_waybill_code'];    //错误类型
                $param['issueImg'] = '';   //签收图片
                $param['frequencyNo'] = ''; //班次(扫描客户端用)

                //必填项
                $must['issueType'] = "错误类型";
                break;
            case 5: //到件
                $param['interfaceversion'] = '1.0';
                $param['sendMobilePhone'] = '';
                $param['receiveMobilePhone'] = '';
                $param['frequencyNo'] = ''; //班次(扫描客户端用)
                $param['lastOrgCode'] = $data['forward_station'] ?: ''; //上一站编码
                $param['weight'] = '';  //重量
                $param['inputWeight'] = ''; //输入重(扫描客户端用)
                $param['length'] = ''; //长
                $param['width'] = ''; //宽
                $param['height'] = ''; //高
                $param['volume'] = ''; //体积

                //必填项
                $must['interfaceversion'] = "接口版本";
                break;
            case 6: //发件
                $param['interfaceversion'] = '1.0';
//                $param['goodsType']        = $data['resType'] ?: 1;   //物品类别
                $param['nextOrgCode'] = $data['next_station'] ?: ''; //下一站编码
                $param['frequencyNo'] = ''; //班次(扫描客户端用)
                $param['routeCode'] = '';   //路由号(网点/中心操作不显示非必填，航空操作必填)
                $param['vehicleId'] = '';   //车辆id
                $param['weight'] = '';  //重量
                $param['inputWeight'] = ''; //输入重(扫描客户端用)
                $param['empCode'] = $data['cm_code']; //派件员

                //必填项
                $must['interfaceversion'] = "接口版本";
                $must['nextOrgCode'] = "下一站编码";
                $must['empCode'] = "派件员编号";
//                $must['goodsType']        = "物品类别";
                break;
            default:
                break;
        }

        $mustVerify = self::mustFieldVerify($must, $param);    //必填项校验
        if ($mustVerify) {
            self::$errMsg = $mustVerify;
            return null;
        }
        return $param;
    }

    /**
     * 必填字段校验
     * @param $must
     * @param $data
     * @return string
     */
    public static function mustFieldVerify($must, $data)
    {
        foreach ($must as $field => $value) {
//            if ((!isset($data[$field]) || empty($data[$field])) && (!isset($data['opRecord'][$field]) || empty($data['opRecord'][$field]))){
            if ((!isset($data[$field]) || empty($data[$field]))) {
                return $value . '不能为空';
            }
        }
    }

    /**
     * 获取业务所需操作码
     * @param $waybillType
     * @return mixed
     */
    public static function getOperateCode($waybillType)
    {
        switch ($waybillType) {
            case 1: //揽件
                $result['mty'] = '0120';
                $result['opCode'] = '120';
                break;
            case 2: //派件
                $result['mty'] = '0710';
                $result['opCode'] = '710';
                break;
            case 3: //签收
                $result['mty'] = '0795';
                $result['opCode'] = '795';
                break;
            case 4: //问题件
                $result['mty'] = '0410';
                $result['opCode'] = '410';
                break;
            case 5: //到件
                $result['mty'] = '0520';
                $result['opCode'] = '520';
                break;
            case 6: //发件
                $result['mty'] = '0210';
                $result['opCode'] = '210';
                break;
            case 10: //登录
                $result['mty'] = '0851';
                break;
            default:
                $result = [];
                break;
        }
        return $result;
    }

    /**
     * 自定义生成随机码
     * @return mixed
     */
    public static function guid()
    {
        mt_srand((double)microtime() * 10000);//optional for php 4.2.0 and up.
        return strtoupper(md5(uniqid(rand(), true)));
    }

    /**
     * 巴枪上传失败原因
     * @var
     */
    public static $errMsg;

    /**
     * @var int
     */
    public static $isLog = 0;

    /**
     * @param $isLog
     */
    public static function setIsLog($isLog)
    {
        self::$isLog = $isLog;
    }
}